/*
 *    Copyright (c) 2018-2025, lengleng All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the pig4cloud.com developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: lengleng (wangiegie@gmail.com)
 */

package com.pig4cloud.pigx.wallet.controller;

import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.customize.i18n.util.MessageUtils;
import com.pig4cloud.pigx.common.customize.mysql.utils.MysqlUtil;
import com.pig4cloud.pigx.common.customize.redis.utils.RedisUtils;
import com.pig4cloud.pigx.common.customize.security.utils.SecurityUtil;
import com.pig4cloud.pigx.common.customize.utils.CommonUtil;
import com.pig4cloud.pigx.common.excel.annotation.ResponseExcel;
import com.pig4cloud.pigx.common.log.annotation.SysLog;
import com.pig4cloud.pigx.common.security.service.PigxUser;
import com.pig4cloud.pigx.contract.constant.ContractMqConstants;
import com.pig4cloud.pigx.wallet.constant.I18nMessages;
import com.pig4cloud.pigx.wallet.constant.WalletConstant;
import com.pig4cloud.pigx.wallet.entity.CapitalAccount;
import com.pig4cloud.pigx.wallet.entity.ContractAccount;
import com.pig4cloud.pigx.wallet.entity.TransferRecord;
import com.pig4cloud.pigx.wallet.service.CapitalAccountService;
import com.pig4cloud.pigx.wallet.service.ContractAccountService;
import com.pig4cloud.pigx.wallet.service.TransferRecordService;
import io.seata.spring.annotation.GlobalLock;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.redisson.api.RLock;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * 划转记录
 *
 * @since 2022-08-16 16:13:21
 */
@RestController
@RequiredArgsConstructor
@RequestMapping("/transferrecord")
@Api(value = "transferrecord", tags = "划转记录管理")
public class TransferRecordController {

    private final TransferRecordService transferRecordService;
    private final CapitalAccountService capitalAccountService;
    private final ContractAccountService contractAccountService;

    private final RabbitTemplate rabbitTemplate;
    private final CommonUtil commonUtil;
    private final RedisUtils redisUtils;
    private final MysqlUtil mysqlUtil;

    /**
     * 分页查询
     *
     * @param page           分页对象
     * @param transferRecord 划转记录
     */
    @ApiOperation(value = "分页查询", notes = "分页查询")
    @GetMapping("/page")
    @PreAuthorize("@pms.hasPermission('wallet_transferrecord_view')")
    public R<Page<TransferRecord>> getTransferRecordPage(Page<TransferRecord> page, TransferRecord transferRecord) {
        return R.ok(transferRecordService.page(page, Wrappers.query(transferRecord)));
    }


    /**
     * 通过id查询划转记录
     *
     * @param id id
     * @return R
     */
    @ApiOperation(value = "通过id查询", notes = "通过id查询")
    @GetMapping("/{id}")
    @PreAuthorize("@pms.hasPermission('wallet_transferrecord_view')")
    public R<TransferRecord> getById(@PathVariable("id") Long id) {
        return R.ok(transferRecordService.getById(id));
    }

    /**
     * 新增划转记录
     *
     * @param transferRecord 划转记录
     * @return R
     */
    @ApiOperation(value = "新增划转记录", notes = "新增划转记录")
    @SysLog("新增划转记录")
    @PostMapping
    @PreAuthorize("@pms.hasPermission('wallet_transferrecord_add')")
    public R<Boolean> save(@RequestBody TransferRecord transferRecord) {
        return R.ok(transferRecordService.save(transferRecord));
    }

    /**
     * 修改划转记录
     *
     * @param transferRecord 划转记录
     * @return R
     */
    @ApiOperation(value = "修改划转记录", notes = "修改划转记录")
    @SysLog("修改划转记录")
    @PutMapping
    @PreAuthorize("@pms.hasPermission('wallet_transferrecord_edit')")
    public R<Boolean> updateById(@RequestBody TransferRecord transferRecord) {
        return R.ok(transferRecordService.updateById(transferRecord));
    }

    /**
     * 通过id删除划转记录
     *
     * @param id id
     * @return R
     */
    @ApiOperation(value = "通过id删除划转记录", notes = "通过id删除划转记录")
    @SysLog("通过id删除划转记录")
    @DeleteMapping("/{id}")
    @PreAuthorize("@pms.hasPermission('wallet_transferrecord_del')")
    public R<Boolean> removeById(@PathVariable Long id) {
        return R.ok(transferRecordService.removeById(id));
    }


    /**
     * 导出excel 表格
     *
     * @param transferRecord 查询条件
     * @return excel 文件流
     */
    @ResponseExcel
    @GetMapping("/export")
    @PreAuthorize("@pms.hasPermission('wallet_transferrecord_export')")
    public List<TransferRecord> export(TransferRecord transferRecord) {
        return transferRecordService.list(Wrappers.query(transferRecord));
    }

    /**
     * 划转
     */
    @ApiOperation(value = "划转", notes = "划转")
    @PostMapping("/transfer")
    @GlobalLock
    public R<Boolean> transfer(@RequestBody TransferRecord transferRecord) {
        PigxUser user = SecurityUtil.getLoginUserNotNull();
        Long userId = user.getId();
        transferRecord.setUserId(userId);

        RLock lock = redisUtils.waitLock(String.valueOf(userId), null);
        try {
            // 开启本地事务
            mysqlUtil.startLocalTx();

            // 资金 -> 合约
            if (WalletConstant.CAPITAL_CONTRACT.equals(transferRecord.getType())) {
                // 减少资金账户额度
                UpdateWrapper<CapitalAccount> updateCapital = new UpdateWrapper<>();
                updateCapital.lambda()
                        .setSql("available = available - " + transferRecord.getQuantity())
                        .eq(CapitalAccount::getUserId, userId)
                        .ge(CapitalAccount::getAvailable, transferRecord.getQuantity());
                if (!capitalAccountService.update(updateCapital)) {
                    throw new RuntimeException(MessageUtils.get(I18nMessages.INSUFFICIENT_ACCOUNT_BALANCE));
                }

                // 增加合约账户额度
                UpdateWrapper<ContractAccount> updateContract = new UpdateWrapper<>();
                updateContract.lambda()
                        .setSql("margin_balance = margin_balance + " + transferRecord.getQuantity())
                        .setSql("wallet_balance = wallet_balance + " + transferRecord.getQuantity())
                        .eq(ContractAccount::getUserId, userId);
                if (!contractAccountService.update(updateContract)) {
                    throw new RuntimeException(MessageUtils.get(I18nMessages.OPERATION_FAILURE));
                }

            } else {
                // 合约 -> 资金
                // 减少合约账户额度
                UpdateWrapper<ContractAccount> updateContract = new UpdateWrapper<>();
                updateContract.lambda()
                        .setSql("margin_balance = margin_balance - " + transferRecord.getQuantity())
                        .setSql("wallet_balance = wallet_balance - " + transferRecord.getQuantity())
                        .eq(ContractAccount::getUserId, userId)
                        .ge(ContractAccount::getMarginBalance, transferRecord.getQuantity())
                        .ge(ContractAccount::getWalletBalance, transferRecord.getQuantity());
                if (!contractAccountService.update(updateContract)) {
                    throw new RuntimeException(MessageUtils.get(I18nMessages.INSUFFICIENT_ACCOUNT_BALANCE));
                }

                // 增加资金账户额度
                UpdateWrapper<CapitalAccount> updateCapital = new UpdateWrapper<>();
                updateCapital.lambda()
                        .setSql("available = available + " + transferRecord.getQuantity())
                        .eq(CapitalAccount::getUserId, userId);
                if (!capitalAccountService.update(updateCapital)) {
                    throw new RuntimeException(MessageUtils.get(I18nMessages.OPERATION_FAILURE));
                }
            }

            // 保存划转记录
            if (!transferRecordService.save(transferRecord)) {
                throw new RuntimeException(MessageUtils.get(I18nMessages.OPERATION_FAILURE));

            }

            // 提交本地事务
            mysqlUtil.commitLocalTx();

            // 发送合约消息队列
            rabbitTemplate.convertAndSend(ContractMqConstants.EXCHANGE_FANOUT_CONTRACT_POSITION_CROSS_CALCULATE,
                    null,
                    userId);
        } catch (Throwable e) {
            // 回滚本地事务
            mysqlUtil.rollbackLocalTx();
            throw e;
        } finally {
            redisUtils.releaseLock(lock);
        }

        return R.ok();
    }
}
